#pragma once

#include <string>
#include <cstdint>
#include <vector>
#include <fstream>
#include <sys/stat.h>

#include "Logger.hpp"
#include "Buffer.hpp"

using namespace std;
using namespace LogMudule;

// 这个模块是⼀个工具模块，主要提供HTTP协议模块所用到的⼀些工具函数
class Tool
{
public:
    // 读取一个文件中的所有内容
    static bool ReadFile(const string &filepath, string *buf)
    {
        // 以二进制方式打开文件，这样普适性更强
        ifstream file(filepath, ios::in | ios::binary);

        // 如果打开失败
        if (file.is_open() == false)
        {
            LOG(LogLevel::WARNING) << filepath << "打开失败";
            return false;
        }

        // 把读指针移动到文件最末尾
        file.seekg(0, file.end);

        // 计算文件大小
        uint64_t file_size = file.tellg();

        // 把读指针移动到文件最开头
        file.seekg(0, file.beg);

        // 确保Buf能存放文件的所有内容
        buf->resize(file_size);

        // 读取文件
        if (!file.read(&(buf->at(0)), file_size))
        {
            LOG(LogLevel::WARNING) << filepath << "读取失败";
            file.close();
            return false;
        }
        // 关闭文件
        file.close();
        return true;
    }
    // 向文件写入
    static bool WriteFile(const string &filepath, string &data)
    {
        // 以二进制方式打开文件，这样普适性更强
        ofstream ofs(filepath, ios::out | ios::binary);
        // 如果打开失败
        if (ofs.is_open() == false)
        {
            LOG(LogLevel::WARNING) << filepath << "打开失败";
            return false;
        }

        // 写入文件
        if (!ofs.write(data.c_str(), data.size()))
        {
            LOG(LogLevel::WARNING) << filepath << "写入失败";
            ofs.close();
            return false;
        }

        // 关闭文件
        ofs.close();
        return true;
    }
    // URL编码
    static string UrlEncode(const string &url, bool convert_space_to_pius)
    {
        string ret;
        for (auto &ch : url)
        {
            // 不编码字符
            if (ch == '.' || ch == '_' || ch == '-' || ch == '~' || isalnum(ch))
            {
                // 直接添加
                ret += ch;
            }
            // 如果为W3C标准
            else if (ch == ' ' && convert_space_to_pius == true)
            {
                // 则空格需要转换成+
                ret += '+';
            }
            else
            {
                // 进行编码
                char tmp[4] = {0};
                snprintf(tmp, 4, "%%%02X", ch);
                ret += tmp;
            }
        }
        return ret;
    }
    // 把一个16进制字符转换成10进制
    static char HEXTOI(char ch)
    {
        if (isalnum(ch))
        {
            return ch - '0';
        }
        else if (ch >= 'a' && ch <= 'f')
        {
            return ch - 'a' + 10;
        }
        else if (ch >= 'A' && ch <= 'F')
        {
            return ch - 'A' + 10;
        }
        else
        {
            LOG(LogLevel::WARNING) << "16进制字符转换成10进制失败!";
            return -1;
        }
    }
    // URL解码
    static string UrlDecode(const string &url, bool convert_space_to_pius)
    {
        string ret;
        int n = url.size();
        for (int i = 0; i < n; i++)
        {
            char ch = url[i];
            // 不编码字符
            if (ch == '.' || ch == '_' || ch == '-' || ch == '~' || isalnum(ch))
            {
                // 直接添加
                ret += ch;
            }
            // 如果为W3C标准
            else if (ch == '+' && convert_space_to_pius == true)
            {
                // 则+需要转换成空格
                ret += ' ';
            }
            else if (ch == '%'&&i+2<n)
            {
                // 进行解码
                char c1 = HEXTOI(url[i + 1]);
                char c2 = HEXTOI(url[i + 2]);
                char tmp = c1 * 16 + c2;
                ret += tmp;

                //%2b,所以向后移动2个字符
                i += 2;
            }
            else
            {
                ret+=ch;
            }
        }
        return ret;
    }
    // 通过HTTP状态码获取状态码描述
    static string StatusCodeDescription(uint32_t code)
    {
        static unordered_map<int, string> statu_description = {
            {100, "Continue"},
            {101, "Switching Protocol"},
            {102, "Processing"},
            {103, "Early Hints"},
            {200, "OK"},
            {201, "Created"},
            {202, "Accepted"},
            {203, "Non-Authoritative Information"},
            {204, "No Content"},
            {205, "Reset Content"},
            {206, "Partial Content"},
            {207, "Multi-Status"},
            {208, "Already Reported"},
            {226, "IM Used"},
            {300, "Multiple Choice"},
            {301, "Moved Permanently"},
            {302, "Found"},
            {303, "See Other"},
            {304, "Not Modified"},
            {305, "Use Proxy"},
            {306, "unused"},
            {307, "Temporary Redirect"},
            {308, "Permanent Redirect"},
            {400, "Bad Request"},
            {401, "Unauthorized"},
            {402, "Payment Required"},
            {403, "Forbidden"},
            {404, "Not Found"},
            {405, "Method Not Allowed"},
            {406, "Not Acceptable"},
            {407, "Proxy Authentication Required"},
            {408, "Request Timeout"},
            {409, "Conflict"},
            {410, "Gone"},
            {411, "Length Required"},
            {412, "Precondition Failed"},
            {413, "Payload Too Large"},
            {414, "URI Too Long"},
            {415, "Unsupported Media Type"},
            {416, "Range Not Satisfiable"},
            {417, "Expectation Failed"},
            {418, "I'm a teapot"},
            {421, "Misdirected Request"},
            {422, "Unprocessable Entity"},
            {423, "Locked"},
            {424, "Failed Dependency"},
            {425, "Too Early"},
            {426, "Upgrade Required"},
            {428, "Precondition Required"},
            {429, "Too Many Requests"},
            {431, "Request Header Fields Too Large"},
            {451, "Unavailable For Legal Reasons"},
            {501, "Not Implemented"},
            {502, "Bad Gateway"},
            {503, "Service Unavailable"},
            {504, "Gateway Timeout"},
            {505, "HTTP Version Not Supported"},
            {506, "Variant Also Negotiates"},
            {507, "Insufficient Storage"},
            {508, "Loop Detected"},
            {510, "Not Extended"},
            {511, "Network Authentication Required"}};
        // 如果找到了
        if (statu_description.find(code) != statu_description.end())
        {
            return statu_description[code];
        }
        else // 如果没找到
        {
            LOG(LogLevel::WARNING) << "状态码转状态码描述失败！";
            return "";
        }
    }
    // 根据文件名获取mime
    static string ExtMime(const string &file_name)
    {
        static unordered_map<string, string> mime_msg = {
            {".aac",
             "audio/aac"},
            {".abw",
             "application/x-abiword"},
            {".arc",
             "application/x-freearc"},
            {".avi",
             "video/x-msvideo"},
            {".azw",
             "application/vnd.amazon.ebook"},
            {".bin",
             "application/octet-stream"},
            {".bmp",
             "image/bmp"},
            {".bz",
             "application/x-bzip"},
            {".bz2",
             "application/x-bzip2"},
            {".csh",
             "application/x-csh"},
            {".css",
             "text/css"},
            {".csv",
             "text/csv"},
            {".doc",
             "application/msword"},
            {".docx",
             "application/vnd.openxmlformats-officedocument.wordprocessingml.document "},
            {".eot",
             "application/vnd.ms-fontobject"},
            {".epub",
             "application/epub+zip"},
            {".gif",
             "image/gif"},
            {".htm",
             "text/html"},
            {".html",
             "text/html"},
            {".ico",
             "image/vnd.microsoft.icon"},
            {".ics",
             "text/calendar"},
            {".jar",
             "application/java-archive"},
            {".jpeg",
             "image/jpeg"},
            {".jpg",
             "image/jpeg"},
            {".js",
             "text/javascript"},
            {".json",
             "application/json"},
            {".jsonld",
             "application/ld+json"},
            {".mid",
             "audio/midi"},
            {".midi",
             "audio/x-midi"},
            {".mjs",
             "text/javascript"},
            {".mp3",
             "audio/mpeg"},
            {".mpeg",
             "video/mpeg"},
            {".mpkg",
             "application/vnd.apple.installer+xml"},
            {".odp",
             "application/vnd.oasis.opendocument.presentation"},
            {".ods",
             "application/vnd.oasis.opendocument.spreadsheet"},
            {".odt",
             "application/vnd.oasis.opendocument.text"},
            {".oga",
             "audio/ogg"},
            {".ogv",
             "video/ogg"},
            {".ogx",
             "application/ogg"},
            {".otf",
             "font/otf"},
            {".png",
             "image/png"},
            {".pdf",
             "application/pdf"},
            {".ppt",
             "application/vnd.ms-powerpoint"},
            {".pptx",
             "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".rar",
             "application/x-rar-compressed"},
            {".rtf",
             "application/rtf"},
            {".sh",
             "application/x-sh"},
            {".svg",
             "image/svg+xml"},
            {".swf",
             "application/x-shockwave-flash"},
            {".tar",
             "application/x-tar"},
            {".tif",
             "image/tiff"},
            {".tiff",
             "image/tiff"},
            {".ttf",
             "font/ttf"},
            {".txt",
             "text/plain"},
            {".vsd",
             "application/vnd.visio"},
            {".wav",
             "audio/wav"},
            {".weba",
             "audio/webm"},
            {".webm",
             "video/webm"},
            {".webp",
             "image/webp"},
            {".woff",
             "font/woff"},
            {".woff2",
             "font/woff2"},
            {".xhtml",
             "application/xhtml+xml"},
            {".xls",
             "application/vnd.ms-excel"},
            {".xlsx",
             "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xml",
             "application/xml"},
            {".xul",
             "application/vnd.mozilla.xul+xml"},
            {".zip",
             "application/zip"},
            {".3gp",
             "video/3gpp"},
            {".3g2",
             "video/3gpp2"},
            {".7z",
             "application/x-7z-compressed"}};
        // 查找文件后缀
        uint64_t pos = file_name.rfind('.');
        if (pos == string::npos)
        {
            LOG(LogLevel::WARNING) << "文件没有后缀名";
            // 返回二进制流mime
            return "application/octet-stream";
        }
        // 截取后缀
        string suffix = file_name.substr(pos);

        // 如果找到了
        if (mime_msg.find(suffix) != mime_msg.end())
        {
            return mime_msg[suffix];
        }
        else // 如果没找到
        {
            LOG(LogLevel::WARNING) << "文件后缀名转mime失败,不存在该后缀的文件名对应的mime";
            // 返回二进制流mime
            return "application/octet-stream";
        }
    }

    // 判断一个文件是否存在并且是目录
    static bool IsDirectory(const string &file_path)
    {
        struct stat st;
        int ret = ::stat(file_path.c_str(), &st);
        if (ret < 0)
        {
            return false;
        }
        return S_ISDIR(st.st_mode);
    }
    // 判断一个文件是否存在并且是普通文件
    static bool IsRegularFile(const string &file_path)
    {
        struct stat st;
        int ret = ::stat(file_path.c_str(), &st);
        if (ret < 0)
        {
            return false;
        }
        return S_ISREG(st.st_mode);
    }
    // 根据分割符分割字符串将分割后的串放进veactor中，返回值是子串的个数
    static int SepStr(const string &str, const string &sep, vector<string> &arr)
    {
        // 分隔符的起始下标
        uint64_t pos = 0;
        // 查找的起始位置
        uint64_t start = 0;

        // 字符串大小
        uint64_t size = str.size();

        // 循环执行
        while ((pos = str.find(sep, start)) != string::npos && pos < size)
        {
            // start==pos就是找到了连续的分割符,不需要添加到结果
            if (start == pos)
            {
                // 更新一下
                start = pos + sep.size();
                continue;
            }
            // 找到了就分割,添加到结果
            arr.emplace_back(str.substr(start, pos - start));
            // 更新一下
            start = pos + sep.size();
        }
        //如果start == size那么就会添加一个空串，没有意义
        if (start != size)
        {
            // 找不到分隔符了,就把str最后剩下的部分也放进arr中
            arr.emplace_back(str.substr(start));
        }
        return arr.size();
    }
};